#include "listener.h"

#include "messaging/endpoint.h"
#include "messaging/drivers/drivers.h"


namespace evm { namespace messaging {

const vector<string> NotificationDispatcher::PRIORITIES = {
    "audit", "debug", "info", "warn", "error", "critical", "sample"
};

NotificationDispatcher::NotificationDispatcher(Targets targets, Endpoints endpoints)
    : BaseDispatcher(endpoints), targets(targets)
{
    for (auto& endpoint: endpoints)
        for (auto& priority: PRIORITIES) {

            /* For notification endpoint, only methods with name
             * in `PRIORITIES` will be remotely callable. */
            if (!endpoint->has(priority))
                continue;

            callbacks_by_priority[priority].emplace_back(
                endpoint->filter_rule,
                [=](const Json& args) -> Json {
                    return (*endpoint)(priority, args);
                }
            );
        }

    for (auto& target: targets)
        for (auto& cp: callbacks_by_priority) {
            targets_priorities.emplace_back(target, cp.first);
        }
}

void NotificationDispatcher::operator()(IncomingMsgPtr incoming) {
    incoming->acknowledge();
    dispatch(incoming->message);
}

bool NotificationDispatcher::dispatch(const Json &message) {
    string priority = message["priority"];

    if (!callbacks_by_priority.count(priority))
        return false;

    for (auto& item: callbacks_by_priority[priority]) {

        FilterPtr& filter_rule = item.first;
        NofifyCallback& callback = item.second;

        /* Only non-null filter rule will be used for matching. */
        if (filter_rule && !filter_rule->match(message))
            continue;

        callback(message);
        return true;
    }

    return false;
}

MessageServerPtr
get_notification_listener(TransportPtr transport, Targets targets, Endpoints endpoints) {
    auto dispatcher = DispatcherPtr(new NotificationDispatcher(targets, endpoints));
    return make_shared<MessageServer>(transport, dispatcher);
}

}}
